package org.example.core;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.example.entity.Columns;
import org.example.entity.dto.ColumnsDto;
import org.example.entity.dto.TablesDto;
import org.example.mapper.ColumnsMapper;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Component
public class ToExcel {

    @Resource
    private ColumnsMapper columnsMapper;

    @Resource
    private ToExcelConfig toExcelConfig;

    private static Map<String, String> map = new HashMap<>();

    static {
        map.put("num", "序号");
        map.put("tableSchema", "数据库名");
        map.put("tableName", "表名");
        map.put("columnName", "列名");
        map.put("columnDefault", "默认值");
        map.put("isNullable", "是否非空");
        map.put("columnType", "列类型");
        map.put("columnKey", "主外键类型");
        map.put("columnComment", "列备注");
    }

    public void toExcel(){
        String filePath = this.toExcelConfig.getFilePath();

        List<List<ColumnsDto>> tables = this.getTables();
        List<List<Map<String, String>>> tableOfMaps = this.getTableOfMaps(tables);
        List<TablesDto> tablesDtos = this.filterAndConvert(tableOfMaps);


        try(ExcelWriter excelWriter = EasyExcel.write(filePath).build()) {
            for (int i = 0; i < tablesDtos.size(); i++) {
                List<List<String>> table = tablesDtos.get(i).getTable();
                WriteSheet writeSheet0 = EasyExcel.writerSheet(i, tablesDtos.get(i).getName()).build();
                excelWriter.write(table, writeSheet0);
            }
            excelWriter.finish();
        }

    }

    /**
     * 获取表的列的信息
     * @return
     */
    public List<List<ColumnsDto>> getTables(){
        //表数据
        List<List<ColumnsDto>> tables = new ArrayList<>();
        //需要导出的表名
        List<String> tableNames = this.getNeedTableNames();
        //数据库名
        String tableSchema = this.toExcelConfig.getTableSchema();
        //列数据
        List<Columns> columns = this.columnsMapper.selectList(new LambdaQueryWrapper<Columns>()
                .eq(Columns::getTableSchema, tableSchema)
                .in(!tableNames.isEmpty(), Columns::getTableName, tableNames));
        //处理数据
        for (String tableName : tableNames) {
            List<ColumnsDto> collect = columns.stream()
                    .filter(it -> it.getTableName().equals(tableName))
                    .map(it -> {
                        ColumnsDto columnsDto = new ColumnsDto();
                        BeanUtil.copyProperties(it, columnsDto);
                        return columnsDto;
                    })
                    .collect(Collectors.toList());
            for (int i = 0; i < collect.size(); i++) {
                collect.get(i).setNum(String.valueOf(i+1));
            }
            tables.add(collect);
        }
        return tables;
    }

    /**
     * 将数据处理成map格式，方便等会筛选过滤
     * @param tables
     * @return
     */
    public List<List<Map<String, String>>> getTableOfMaps(List<List<ColumnsDto>> tables){
        List<List<Map<String, String>>> tableOfMaps = new ArrayList<>();
        tables.forEach(its -> {
            List<Map<String, String>> table = new ArrayList<>();
            its.forEach(it -> {
                try {
                    Map<String, String> map = objectToMap(it);
                    table.add(map);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
            tableOfMaps.add(table);
        });
        return tableOfMaps;
    }

    public List<TablesDto> filterAndConvert(List<List<Map<String, String>>> tables){
        List<String> columns = this.toExcelConfig.getColumns();
        List<TablesDto> lists = new ArrayList<>();
        tables.forEach(its -> {
            TablesDto tablesDto = new TablesDto();
            List<List<String>> results = new ArrayList<>();

            List<String> head = new ArrayList<>();
            head.add("数据库名");
            head.add(its.get(0).get("tableName"));
            results.add(head);

            //写入表头
            List<String> name = new ArrayList<>();
            columns.forEach(it -> {
                name.add(map.get(it));
            });
            results.add(name);

            //写入数据
            its.forEach(it -> {
                List<String> result = new ArrayList<>();
                //填入数据
                for (String column : columns) {
                    result.add(it.get(column));
                }
                results.add(result);
            });
            tablesDto.setTable(results);
            tablesDto.setName(its.get(0).get("tableName"));
            lists.add(tablesDto);
        });
        return lists;
    }

    /**
     * 查出来需要导出的表名称
     * @return List
     */
    public List<String> getNeedTableNames(){
        //需要导出的表名
        List<String> tableNames = this.toExcelConfig.getTableNames();
        //需要忽略的表名
        List<String> ignoreTableNames = this.toExcelConfig.getIgnoreTableNames();
        //数据库名
        String tableSchema = this.toExcelConfig.getTableSchema();

        if (tableNames.isEmpty()){
            return this.columnsMapper.selectList(new LambdaQueryWrapper<Columns>()
                    .eq(Columns::getTableSchema, tableSchema)
                    .notIn(!ignoreTableNames.isEmpty(), Columns::getTableName, ignoreTableNames))
                    .stream().map(Columns::getTableName)
                    .distinct()
                    .collect(Collectors.toList());
        }else {
            return tableNames;
        }
    }

    /**
     * 对象转map
     * @param obj
     * @return
     * @throws Exception
     */
    public static Map<String, String> objectToMap(Object obj) throws Exception {
        if (obj == null) {
            return null;
        }
        Map<String, String> map = new HashMap<String, String>();

        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            map.put(field.getName(), (String) field.get(obj));
        }
        return map;
    }

}
